#include "solver.h"

Solver::Solver(std::string& str)
{
	parseFunction(str);		
}

Solver::~Solver()
{
	delete function;
}

//интегрирование методом трапеций
double Solver::integrate(double a, double b, double eps)
{
	double res = 0;
	double x = a;
	while(x < b){
		res += ( f(x + eps) + f(x) ) / 2;
		x += eps;
	}
	res *= eps;
	return res;
}

bool Solver::solve(double a, double b, double eps, double& answer)
{
	//концы функции одинаковых знаков или неверно указан диапазон - решений нет	
	if (( (f(a) > 0) && (f(b) > 0) ) ||
	    ( (f(a) < 0) && (f(b) < 0) ) ||
	       a > b)
		return false;

	//до достижения точности
	while( b - a > eps ){		
		//делим отрезок пополам
		double k = (b + a) / 2;
		//если в левой половине знак не меняется, то изменяем левую границу, иначе правую.
		if (( (f(a) > 0) && (f(k) > 0) ) ||
		    ( (f(a) < 0) && (f(k) < 0) ))
			a = k;	
		else
			b = k; 
	};	

	answer = (a + b)/2;
	return true;
}

double Solver::f(double x)
{
	return function->value(x);
}




//_____PRIVATE:____

//стек подфункций
void Solver::pushFunc(Function* func)
{
	subFunctions[funcStackLength] = func;
	funcStackLength++;
}

Function* Solver::popFunc()
{
	funcStackLength--;
	return subFunctions[funcStackLength];
}

//стек операций
void Solver::pushOp(char operation)
{
	operations[opStackLength] = operation;
	opStackLength++;
}

char Solver::popOp()
{
	opStackLength--;		
	return operations[opStackLength];		
}

//конвертирование лексемы в double
double Solver::stringToDouble(std::string& str)
{
	double res = 0;
	int pointPos = 0;
	for(int i = 0; i < str.size(); ++i){
		if(str[i] == '.'){
			pointPos = i;
			continue;
		}
		
		if(i < pointPos || pointPos == 0){
			res *= 10;
			res += str[i] - '0';
		}
		else {
			double q = str[i] - '0';
			for(int j = 0; j < (i - pointPos); j++)
				q/=10;
			res += q;
		}			
	}
	return res;
}

//преобразование строки с функцией в дерево классов операций
void Solver::parseFunction(std::string& str)
{
	funcStackLength = 0;
	opStackLength = 0;
	currTokPos = 0;	
	int prevOperationType = NONE;	

	do{			
		getToken(str);

		int type = getTokenType(currToken);
			if(type == CONST){
			double val = stringToDouble(currToken);
			Function* func = new Const(val); 
			pushFunc(func);
		}
		
		if(type == VAR){
			Function* func = new Var(); 
			pushFunc(func);
		}

		if(type == HIGHT_OPERATION){   // * или /
			if(prevOperationType == HIGHT_OPERATION && funcStackLength >= 2){
				Function* a = popFunc();
				Function* b = popFunc();

				Function* func;
				if (currToken == "*")				
					func = new Multiply(a,b);
				else if (currToken == "/")				
					func = new Divide(a,b);
				pushFunc(func);
			}
			else {					
				pushOp(currToken[0]);				
			}
			prevOperationType = type;
		}

		if(type == LOW_OPERATION){   // + или -
			if(prevOperationType == HIGHT_OPERATION)  //до этого в стеке была операция с большим приоритетом - выполняем сперва её
			{
				Function* a = popFunc();
				Function* b = popFunc();
				char operation = popOp();	
					Function* func;
					if (operation == '*')				
					func = new Multiply(a,b);
				else if (operation == '/')				
					func = new Divide(a,b);
				pushFunc(func);					
			}
			pushOp(currToken[0]);				
			prevOperationType = type;				
		}

	}while(currToken != "");

	while(opStackLength > 0) //дочищаем стек от оставшихся операций
	{							
		Function* a = popFunc();
		Function* b = popFunc();
			Function* func;
			char operation = popOp();
	
		if (operation == '+')				
			func = new Plus(a,b);
		if (operation == '-')				
			func = new Minus(a,b);
		if (operation == '*')				
			func = new Multiply(a,b);
		if (operation == '/')				
			func = new Divide(a,b);
		pushFunc(func);
	
	}
		function = popFunc();
}

//получение типа текущей просматриваемой лексемы	
int Solver::getTokenType(std::string str)
{
	if(str[0] >= '0' && str[0] <= '9')
		return CONST;
	if(str == "x")
		return VAR;
	if(str == "+" || str == "-")
		return LOW_OPERATION;
	if(str == "*" || str == "/")
		return HIGHT_OPERATION;
	return NONE;
}

/* получить следующую лексему.
* увеличивает currTokPos. 
*возвращает пустую строку при достижении конца строки */
void Solver::getToken(std::string str)
{
	currToken = "";

	if(currTokPos > str.size())
		return;

	if(str[currTokPos] >= '0' && str[currTokPos] <= '9'){			
		while((str[currTokPos] >= '0' && str[currTokPos] <= '9')
        	    || str[currTokPos] == '.'){
			currToken = currToken + str[currTokPos];
			currTokPos++;
		}
		return;
	}

	if(str[currTokPos] == '+' ||
	   str[currTokPos] == '-' ||
 	   str[currTokPos] == '*' ||
	   str[currTokPos] == '/' ||
	   str[currTokPos] == 'x'){
		currToken = str[currTokPos];
		currTokPos++;
		return;
	}
}


